package com.googlecode.imanager.core.base;

import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;

import com.googlecode.imanager.client.Application;
import com.googlecode.imanager.client.Control;
import com.googlecode.imanager.client.Document;
import com.googlecode.imanager.client.action.Action;
import com.googlecode.imanager.client.bridge.IApplication;
import com.googlecode.imanager.client.bridge.style.IParentThemeHandler;
import com.googlecode.imanager.client.event.EvaluationPhase;
import com.googlecode.imanager.client.event.KeyEvent;
import com.googlecode.imanager.client.event.KeyEventType;
import com.googlecode.imanager.client.event.MouseEvent;
import com.googlecode.imanager.client.event.MouseEventType;
import com.googlecode.imanager.client.event.PhaseEvent;
import com.googlecode.imanager.client.io.Path;
import com.googlecode.imanager.client.media.Image;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class BridgeApplication extends BridgeComponent implements IApplication, IParentThemeHandler {

    private static final Path BASE_PATH;
    
    private BridgePhaseEvent phaseEvent;
    private BridgeMouseEvent mouseEvent;    
    private BridgeKeyEvent keyEvent;    
    
    private OutputStream output;
    private final DefaultManager manager;
    
    public BridgeApplication(DefaultManager manager, Application application, OutputStream output) {
        this.manager = manager;
        this.output = output;
        if (application != null){
            manager.setImpl(application, this);
        }
    }
    
    public DefaultManager getManager() {
        return manager;
    }
    
    public OutputStream getOutput() {
        return output;
    }

    @Override
    protected void destroy() {
        output = null;
        super.destroy();
    }

    protected void beforeGenerateBridge(Document document) {
    }
    
    protected void generateBridge(Document document) throws Exception {
        synchronized (TREE_LOCK) {
            generateBridgeStub(document);
            generateBridgePrint(document);
        }
    }

    protected void afterGenerateBridge(Document document){ 
    }

    protected final void generateBridgeStub(Document document) {
        getManager().generateChildrenBridgeImplementation(document, this);
    }
    
    protected void generateBridgePrint(Document document) throws Exception {
        firePhaseEvent(document, EvaluationPhase.DOCUMENT_START);        
        firePhaseEvent(document, EvaluationPhase.DOCUMENT_END);
    }

    protected void beforeValidateRootImplementation(Control owner) {
    }

    protected void afterValidateRootImplementation(Control owner) {
    }

    protected void beforeCreateImplementation(Control owner) {
        /*if (owner instanceof Document){
            Document doc = (Document) owner;
            if (doc.getImplementationType() != BridgeType.EMPTY){
                firePhaseEvent(doc, EvaluationPhase.APPLICATION_END);
            }
        }*/
    }
    
    protected abstract BridgeControl createImplementation(Control source);

    protected void afterCreateImplementation(Control owner) {
        /*if (owner instanceof Document){
            Document doc = (Document) owner;
            if (doc.getImplementationType() != BridgeType.EMPTY){
                firePhaseEvent(doc, EvaluationPhase.APPLICATION_START);
            }
        }*/
    }

    protected void startFireUIEvent(){
    }

    protected void endFireUIEvent(){
    }
    
    static {
        URL url = BridgeApplication.class.getResource("/");
        BASE_PATH = new Path(url == null ? "" : url.toExternalForm());
    }

    @Override
    public Path getBasePath() {
        return BASE_PATH;
    }

    public abstract void loadImageContent(DefaultImage abstractImage);

    public URL getURLFromImage(Image image){
        if (image == Image.EMPTY_IMAGE){
            return null;
        }
        
        String addr = image.getPath().toString();
        URL result;
        
        try {
            result = new URL(addr);
        } catch (MalformedURLException e) {
            try {
                result = new URL(new Path(image.getOwner().getBasePath(), addr).getValue());
            } catch (MalformedURLException e1) {
                result = getAlternativeURLFromPath(addr);
            }
        }
        
        return result;
    }

    public URL getURLFromPath(String path){
        try {
            return new URL(path);
        } catch (MalformedURLException e) {
            return getAlternativeURLFromPath(path);
        }
    }

    protected URL getAlternativeURLFromPath(String path) {
        try {
            // TODO pegar path do contextClassLoader
            return new URL(getClass().getResource("/"), path);
        } catch (MalformedURLException e1) {
            return null;
        }
    }
    
    protected boolean executeAction(Action action, Control sender) {
        return false;
    }
    
    public PhaseEvent firePhaseEvent(Document target, EvaluationPhase eventType) {
        BridgePhaseEvent phaseEvent = this.phaseEvent;
        this.phaseEvent = null;
        
        if (phaseEvent == null){
            phaseEvent = new BridgePhaseEvent(target, eventType);
        } else {
            phaseEvent.reset();
            phaseEvent.setTarget(target);
            phaseEvent.setEventType(eventType);
        }
        
        getManager().firePhaseEvent(phaseEvent);
        
        this.phaseEvent = phaseEvent;
        return phaseEvent;
    }

    public MouseEvent fireMouseEvent(Control target, int x, int y, int clickCount, MouseEventType eventType) {
        if (mouseEvent == null){
            mouseEvent = new BridgeMouseEvent(target, x, y, clickCount, eventType);
        } else {
            mouseEvent.reset();
            mouseEvent.setTarget(target);
            mouseEvent.setEventType(eventType);
            mouseEvent.setX(x);
            mouseEvent.setY(y);
            mouseEvent.setClickCount(clickCount);
        }
        getManager().fireMouseEvent(mouseEvent);
        return mouseEvent;
    }
    
    public KeyEvent fireKeyEvent(Control target, int keyCode, char keyChar, KeyEventType eventType) {
        if (keyEvent == null){
            keyEvent = new BridgeKeyEvent(target, keyCode, keyChar, eventType);
        } else {
            keyEvent.reset();
            keyEvent.setTarget(target);
            keyEvent.setEventType(eventType);
            keyEvent.setKeyCode(keyCode);
            keyEvent.setKeyChar(keyChar);
        }
        getManager().fireKeyEvent(keyEvent);
        return keyEvent;
    }
    
    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    static final class BridgePhaseEvent extends PhaseEvent {
        
        public BridgePhaseEvent(Document target, EvaluationPhase phase) {
            super(target, phase);
        }

        @Override
        public void setEventType(EvaluationPhase phase) {
            super.setEventType(phase);
        }

        public void setTarget(Document target) {
            super.setTarget(target);
        }

        @Override
        public void reset() {
            super.reset();
        }

    }

    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    static final class BridgeMouseEvent extends MouseEvent {

        public BridgeMouseEvent(Control target, int x, int y, int clickCount, MouseEventType eventType) {
            super(target, x, y, clickCount, eventType);
        }

        @Override
        public void reset() {
            super.reset();
        }

        @Override
        public void setEventType(MouseEventType eventType) {
            super.setEventType(eventType);
        }

        @Override
        public void setTarget(Control target) {
            super.setTarget(target);
        }
        
    }
    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    static final class BridgeKeyEvent extends KeyEvent {

        public BridgeKeyEvent(Control target, int keyCode, char keyChar, KeyEventType eventType) {
            super(target, keyCode, keyChar, eventType);
        }

        @Override
        public void setEventType(KeyEventType eventType) {
            super.setEventType(eventType);
        }

        @Override
        public void setTarget(Control target) {
            super.setTarget(target);
        }
        
        @Override
        public void reset() {
            super.reset();
        }

    }

}
